package ronin.util;

import java.util.TreeSet;
import java.util.Vector;
import ronin.util.EntityManager;
import ronin.util.Telegram;
import ronin.actor2d.SmartActor;
import ronin.graphics.GrimaceGamelet;

public class MessageDispatcher {
	
	/** 
	 * THIS IS A SINGLETON, hence we have a static reference. Once the 
	 * class is loaded, the static reference is created to avoid any Syncronization 
	 * issues that may cause a problem.*/
	private static final MessageDispatcher messageDispatcher = new MessageDispatcher();
	
	private GrimaceGamelet gamelet;
	
	/** 
	 * Your gamelet plans to use message passing, it should register
	 * itself  with the dispatcher. GrimaceGamelet does this automatically in
	 * its run function after AdvancedTimer has been created.
	 */
	public void registerGamelet(GrimaceGamelet theGamelet) {
		this.gamelet = theGamelet;
	}
	/** 
	 * PriorityQueue sorts messages by their dispatch time. 
	 *
	 * FYI: TreeSet stores objects in a sorted manner. TreeSet 
	 * stores its elements in a tree and they are automatically 
	 * arranged in a sorted order.
	 *
	 * TreeSet is not synchronized. If more than one thread wants 
	 * to access it at the same time then it must be synchronized 
	 * externally.
	 */
	private TreeSet<Telegram> priorityQueue;
	
	
	private MessageDispatcher() {
		priorityQueue = new TreeSet<Telegram>();
	}
	
	/**
	 * Returns the Singleton Dispatcher so a  message can be sent.
	 */
	public static MessageDispatcher getInstance(){
		return messageDispatcher;
	}
	
	/**
	 * This method is utilized by Dispatch Message and DispatchDelayedMessages. 
	 * It calls the message handling member function of the receiving entity with 
	 * the newly created Telegram.
	 */
	 void discharge(SmartActor receiver, Telegram message){
	 		receiver.handleMessage(message);
	 }
	 
	 /**
	  * Create a new message and sends it immediately if the delay is zero, else it will send
	  */
	 public void newMessage(double delay, // in seconds due to Timer.getClockTicks being used for delayed dispatch in Gamelet
	 					int sender, 
	 					int receiver,
	 					int message,
	 					Vector extraInfo){
	 
	 	SmartActor recipient = (SmartActor) EntityManager.getInstance().getByID(receiver);	 	
		Telegram newMsg = new Telegram(0.0, sender, receiver, message, extraInfo);
	 	
	 	if(delay <= 0.0)
	 	{
	 		discharge(recipient, newMsg);
	 	}
	 	else 
	 	{
	 		double currentTime = gamelet.totalElapsedTime;
	 		newMsg.dispatchTime = currentTime + delay; 
	 		priorityQueue.add(newMsg);
	 	}
	 }
	 
	 /**
	  * Dispatch Delayed Messages is called in the gamelet run game method. 
	  */
	 public void dispatchDelayedMessages() {
	 	
	 	double currentTime = gamelet.totalElapsedTime;
	 	double dispatchTime = priorityQueue.first().dispatchTime;
	 	
	 	//  while dispatch time has passed and dispatch time is atleast greater than zero
	 	while( (dispatchTime < currentTime) && (dispatchTime > 0) )
	 	{
	 		// get telegram
	 		Telegram theMessage = priorityQueue.first();
	 		// find the recipient
	 		SmartActor recipient = (SmartActor) EntityManager.getInstance().getByID(theMessage.receiver);
	 		// handover the message		
	 		discharge(recipient, theMessage);		
	 		// remove the first Object in the queue.
	 		priorityQueue.remove(priorityQueue.first());
	 		// set dispatch time to the new first elements so the loop can do its check and balance :)
	 		dispatchTime = priorityQueue.first().dispatchTime;
	 	}
	 }	  
}
